
#include "FaceRecongnizer_jni.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>

#include <iostream>
#include <fstream>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect.hpp>
#include <opencv2/core.hpp>


#include <android/log.h>


#include <vector>
#include <cstdio>


#define XML_PATH "/storage/emulated/0/FaceData_register/haarcascade_frontalface_alt2.xml"
#define FACE_DIR "/storage/emulated/0/FaceData_fisher/"
#define FACE 	   "/storage/emulated/0/FaceCap/face.jpg"
#define MAXFILENUM   500
#define MAXFILELEN   512
#define RECTANGLE_X  100
#define RECTANGLE_Y  100

#include "mingxin/mingxin.h"
#include "mingxin/JniUtils.h"
using namespace cv;
using namespace std;


MingXin mingXin;

char img_path[MAXFILENUM][MAXFILELEN];
char facedone[MAXFILENUM][MAXFILELEN];

int  img_num = 0;

int fingerprint(Mat src, Mat* hash);
int readFileList(const char *basePath);
bool deviceCheckStatus=false;


#if 1
JNIEXPORT jint JNICALL Java_com_rico_ricorecognize_FaceRecongnizer_nativeLibInit(JNIEnv *env, jclass cls, jstring jlicense)
{

    string license=Jstring2string(env,jlicense);
    //LOGI("input license:%s",license.c_str());
    deviceCheckStatus=mingXin.check(license);
    if(deviceCheckStatus){
        return (jint)1;
    }

    return (jint)0;

}
#endif

int FaceDetectandSaveRegisterFacePicture(const char* currentpathdir) {
    if(!deviceCheckStatus){
        return -1;
    }
    if((access(currentpathdir, F_OK )) == -1 )
    {
        //LOGE( "dir %s is not existed",currentpathdir);
        return -1;
    }


    bool stop = false;

    CascadeClassifier cascade;
    bool loadresult= cascade.load(XML_PATH);
    if(loadresult == false)
    {
        //LOGE("can not load haarcascade_frontalface_alt2.xml");
        return -1;
    }

    Mat srcImage, grayImage;

    for(int i=0; i<img_num; i++)
    {
        srcImage = imread(img_path[i]);
        if(srcImage.data == 0)
        {
            //LOGE( "can not load %s", img_path[i]);
            return -1;
        }


        grayImage = srcImage.clone();

        //grayImage.create(srcImage.size(), srcImage.type());
        //cvtColor(srcImage, grayImage, CV_BGR2GRAY); // 生成灰度图，提高检测效率
        //equalizeHist(grayImage, grayImage);


        vector<Rect> rect;
        cascade.detectMultiScale(grayImage, rect, 1.1, 2, 0);  // 分类器对象调用

        if(rect.size() == 0)
        {
            continue;
        }

        /*
        Rect newrect =rect[0];
        int x1= newrect.x-5;
        if(x1 <= 0)
            x1=0;

        int y1= newrect.y-5;
        if(y1 <= 0)
            y1=0;

        int w1= newrect.width+15;
        if(x1+w1 >= grayImage.cols)
            w1 = grayImage.cols - x1;

        int h1= newrect.height+15;
        if(y1+h1 >= grayImage.rows)
            h1 = grayImage.rows - y1;


        Rect tmprect= Rect(x1, y1, w1, h1);
        */
        Mat sub = Mat(grayImage,rect[0]);
        //Mat sub = Mat(grayImage,tmprect);

        Mat mat = Mat();
        Size size = Size(RECTANGLE_X, RECTANGLE_Y);
        resize(sub, mat, size);

        imwrite(facedone[i], mat);
        //LOGE( "save picture: %s", facedone[i]);

    }
    return 0;


}

JNIEXPORT jint JNICALL Java_com_rico_ricorecognize_FaceRecongnizer_nativeProcessOrignalPhotos
        (JNIEnv *env, jclass cls, jstring photodir) {
    if(!deviceCheckStatus){
        return (jint)-1;
    }

    const char* currentpathdir = env->GetStringUTFChars(photodir, NULL);

    if(-1 == readFileList(currentpathdir))
    {
        return (jint)-1;
    }

    if(-1 == FaceDetectandSaveRegisterFacePicture(currentpathdir))
    {
        return (jint)-1;
    }

    return (jint)0;

}


int FaceDetectandSaveGrabFacePicture(const char* currentfilename) {


    if((access(currentfilename, F_OK )) == -1 )
    {
        //LOGE( "file %s is not existed",currentfilename);
        return -1;
    }


    bool stop = false;

    CascadeClassifier cascade;
    bool loadresult= cascade.load(XML_PATH);
    if(loadresult == false)
    {
        //LOGE("can not load haarcascade_frontalface_alt2.xml");
        return -1;
    }

    Mat srcImage, grayImage;


    srcImage = imread(currentfilename);
    if(srcImage.data == 0)
    {
        //LOGE( "can not load %s", currentfilename);
        return -1;
    }


    grayImage = srcImage.clone();

    //grayImage.create(srcImage.size(), srcImage.type());
    //cvtColor(srcImage, grayImage, CV_BGR2GRAY); // 生成灰度图，提高检测效率
    //equalizeHist(grayImage, grayImage);


    vector<Rect> rect;
    cascade.detectMultiScale(grayImage, rect, 1.1, 2, 0);  // 分类器对象调用


    if(rect.size() == 0)
    {
        return -1;
    }


    Rect newrect =rect[0];

    //LOGE( "rect.x: %d", newrect.x);
    //LOGE( "rect.y: %d", newrect.y);
    //LOGE( "rect.width: %d", newrect.width);
    //LOGE( "rect.height: %d", newrect.height);
    /*
    int x1= newrect.x-5;
    if(x1 <= 0)
        x1=0;

    int y1= newrect.y-5;
    if(y1 <= 0)
        y1=0;


    int w1= newrect.width+15;
    if(x1+w1 >= grayImage.cols)
        w1 = grayImage.cols - x1;

    int h1= newrect.height+15;
    if(y1+h1 >= grayImage.rows)
        h1 = grayImage.rows - y1;

    //LOGE( "rayImage.cols: %d", grayImage.cols);//x1+w1
    //LOGE( "rayImage.rows: %d", grayImage.rows);//y1+h1

    Rect tmprect= Rect(x1, y1, w1, h1);
    */
    Mat sub = Mat(grayImage,rect[0]);
    //Mat sub = Mat(grayImage,tmprect);
    Mat mat = Mat();
    Size size = Size(RECTANGLE_X, RECTANGLE_Y);
    resize(sub, mat, size);

    imwrite(FACE, mat);

    LOGE( "save picture: %s", FACE);


    return 0;


}

JNIEXPORT jint JNICALL Java_com_rico_ricorecognize_FaceRecongnizer_nativeProcessGrabPhotos
        (JNIEnv *env, jclass cls, jstring photoname) {
    if(!deviceCheckStatus){
        return (jint)-1;
    }
    const char* currentpathname = env->GetStringUTFChars(photoname, NULL);

    if(-1 == FaceDetectandSaveGrabFacePicture(currentpathname))
    {
        return (jint)-1;
    }

    return (jint)0;

}




int readFileList(const char *basePath)
{
#if 1
    DIR *dir;
    struct dirent *ptr;

    if ((dir=opendir(basePath)) == NULL)
    {
        //LOGE("Open dir error...");
        return -1;
    }

    while ((ptr=readdir(dir)) != NULL)
    {
        if(strcmp(ptr->d_name,".")==0 || strcmp(ptr->d_name,"..")==0)    ///current dir OR parrent dir
        {
            continue;
        }
        else if(ptr->d_type == 8)    ///file
        {
            if(strstr(ptr->d_name, ".jpg") !=NULL || strstr(ptr->d_name, ".png") !=NULL)
            {
                strcpy(img_path[img_num],basePath);
                //strcat(img_path[img_num++],ptr->d_name);
                strcat(img_path[img_num],ptr->d_name);

                strcpy(facedone[img_num],FACE_DIR);
                strcat(facedone[img_num],ptr->d_name);

                img_num ++;
            }

        }
        else
        {
            continue;
        }
    }
    closedir(dir);
    return img_num;
#endif
}


int fingerprint(Mat src, Mat* hash)
{
    cv::resize(src, src, Size(32, 32));
    src.convertTo(src, CV_32F);
    Mat srcDCT;
    dct(src, srcDCT);
    srcDCT = abs(srcDCT);
    double sum = 0;
    for (int i = 0; i < 8; i++)
        for (int j = 0; j < 8; j++)
            sum += srcDCT.at<float>(i,j);

    double average = sum/64;
    Mat phashcode= Mat::zeros(Size(8, 8), CV_8U);
    for (int i = 0; i < 8; i++)
        for (int j = 0; j < 8; j++)
            phashcode.at<char>(i,j) = srcDCT.at<float>(i,j) > average ? 1:0;

    *hash = phashcode.reshape(0,1).clone();
    return 0;
}

int GetHammingDistace(const char * filename1, const char * filename2)
{
    Mat src = imread(filename1, 0);
    if(src.empty())
    {
        //LOGE("the catch image is not exist");
        return -1;
    }

    Mat train = imread(filename2, 0);
    if(train.empty())
    {
        //LOGE("the train image is not exist");
        return -1;
    }
    Mat srchash, trainhash;
    fingerprint(src, &srchash);
    fingerprint(train, &trainhash);

    int d = 0;
    for (int n = 0; n < srchash.size[1]; n++)
        if (srchash.at<uchar>(0,n) != trainhash.at<uchar>(0,n)) d++;

    LOGE("result(distance) =%d", d);
    return d;

}

int CheckPictureMatchDegree(const char * filename, const char * path) {

    double  similarity = 0.0;
    Mat src = imread(filename, 0);
    if(src.empty())
    {
        //LOGE("the catch image is not exist");
        return -1;
    }

    Mat train = imread(path, 0);
    if(train.empty())
    {
        //LOGE("the train image is not exist");
        return -1;
    }

    IplImage * image= cvLoadImage(filename);
    if(image == NULL)
    {
        LOGE("can not load filename");
        return -1;
    }
    IplImage * image2= cvLoadImage(path);
    if(image2 == NULL)
    {
        LOGE("can not load path");
        return -1;
    }

    int hist_size=256;
    float range[] = {0,255};
    float* ranges[]={range};

    IplImage* gray_plane = cvCreateImage(cvGetSize(image),8,1);
    cvCvtColor(image,gray_plane,CV_BGR2GRAY);
    CvHistogram* gray_hist = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
    cvCalcHist(&gray_plane,gray_hist,0,0);


    IplImage* gray_plane2 = cvCreateImage(cvGetSize(image2),8,1);
    cvCvtColor(image2,gray_plane2,CV_BGR2GRAY);
    CvHistogram* gray_hist2 = cvCreateHist(1,&hist_size,CV_HIST_ARRAY,ranges,1);
    cvCalcHist(&gray_plane2,gray_hist2,0,0);

    similarity=cvCompareHist(gray_hist,gray_hist2,CV_COMP_CORREL);

    LOGE("similarity =%f", similarity);

    return (int )(similarity*100);

}

Scalar getMSSIM(const char * imagePatha, const char * imagePathb)
{

    Mat i1=imread(imagePatha);

    Mat i2=imread(imagePathb);

    const double C1 = 6.5025, C2 = 58.5225;

    int d = CV_32F;

    Mat I1, I2;

    i1.convertTo(I1, d);

    i2.convertTo(I2, d);

    Mat I2_2   = I2.mul(I2);

    Mat I1_2   = I1.mul(I1);

    Mat I1_I2  = I1.mul(I2);

    Mat mu1, mu2;

    GaussianBlur(I1, mu1, Size(11, 11), 1.5);

    GaussianBlur(I2, mu2, Size(11, 11), 1.5);

    Mat mu1_2   =   mu1.mul(mu1);

    Mat mu2_2   =   mu2.mul(mu2);

    Mat mu1_mu2 =   mu1.mul(mu2);

    Mat sigma1_2, sigma2_2, sigma12;

    GaussianBlur(I1_2, sigma1_2, Size(11, 11), 1.5);

    sigma1_2 -= mu1_2;

    GaussianBlur(I2_2, sigma2_2, Size(11, 11), 1.5);

    sigma2_2 -= mu2_2;

    GaussianBlur(I1_I2, sigma12, Size(11, 11), 1.5);

    sigma12 -= mu1_mu2;

    Mat t1, t2, t3;

    t1 = 2 * mu1_mu2 + C1;

    t2 = 2 * sigma12 + C2;

    t3 = t1.mul(t2);

    t1 = mu1_2 + mu2_2 + C1;

    t2 = sigma1_2 + sigma2_2 + C2;

    t1 = t1.mul(t2);

    Mat ssim_map;

    divide(t3, t1, ssim_map);

    Scalar mssim = mean( ssim_map );
    //LOGE("result1 =%f", mssim.val[0]);
    //LOGE("result2 =%f", mssim.val[1]);
    //LOGE("result3 =%f", mssim.val[2]);
    return mssim;

}

JNIEXPORT jint JNICALL Java_com_rico_ricorecognize_FaceRecongnizer_nativeGetMatchResult(
        JNIEnv *env, jclass cls, jstring currentpath, jstring trainpath, jint limit, jint limit1){
    if(!deviceCheckStatus){
        return (jint)0;
    }
    int result = 0;
    int d = 0;
    int match =0;

    const char* currentpath1 = env->GetStringUTFChars(currentpath, NULL);

    const char* trainpath1 = env->GetStringUTFChars(trainpath, NULL);

    d = GetHammingDistace(currentpath1, trainpath1);

    if((access(currentpath1, F_OK )) == -1 )
    {
        LOGE( "file %s is not existed",currentpath1);
        return -1;
    }

    if((access(trainpath1, F_OK )) == -1 )
    {
        LOGE( "file %s is not existed",trainpath1);
        return -1;
    }

    if(limit1 == 0) //只比较汉明距离
    {

        if ( d <= limit && d >= 0) {

            result = 1;

        } else {

            result = 0;
        }

    } else  { // 加入 结构化相似度
        Scalar mssim= getMSSIM(currentpath1, trainpath1);
        match = (int)((mssim.val[0]+mssim.val[1]+mssim.val[2])/3*100);
        LOGE("result(match) =%d", match);

        //match = CheckPictureMatchDegree(currentpath1, trainpath1);
        if ( (d <= limit && d >= 0)&&(match >= limit1)) {

            result = 1;

        } else {

            result = 0;
        }

    }

    LOGE("result =%d", result);

    return result;

}

